//
//  WXPayClient.m
//  WechatPayDemo
//
//  Created by Alvin on 3/22/14.
//  Copyright (c) 2014 Alvin. All rights reserved.
//

#import "WXPayClient.h"
#import "CommonUtil.h"
#import "OauthKeys.h"
#import "JXHttpTool.h"
#import "AFNetworking.h"

NSString *AccessTokenKey = @"access_token";
NSString *PrePayIdKey = @"prepayid";
NSString *errcodeKey = @"errcode";
NSString *errmsgKey = @"errmsg";
NSString *expiresInKey = @"expires_in";

@interface WXPayClient ()

@property (nonatomic, copy) NSString *timeStamp;
@property (nonatomic, copy) NSString *nonceStr;
@property (nonatomic, copy) NSString *traceId;

//订单参数
@property (nonatomic, copy) NSString *orderNum;
@property (nonatomic, copy) NSString *prodName;
@property (nonatomic, copy) NSString *money;
@property (nonatomic, copy) NSString *notify_url;
@property(nonatomic,weak)UIView *container;
@end

@implementation WXPayClient

#pragma mark - Public
-(void)pay:(NSString *)orderNum prodName:(NSString *)prodName money:(NSString *)money notifyUrl:(NSString *)notifyUrl container:(UIView *)container
{
    self.orderNum = orderNum;
    self.prodName = prodName;
    self.money = money;
    self.notify_url = notifyUrl;
    self.container = container;
    //获取token
    [self getAccessToken];
}

#pragma mark - 生成各种参数
- (NSString *)genTimeStamp
{
    return [NSString stringWithFormat:@"%.0f", [[NSDate date] timeIntervalSince1970]];
}

/**
 * 注意：商户系统内部的订单号,32个字符内、可包含字母,确保在商户系统唯一
 */
- (NSString *)genNonceStr
{
    return [CommonUtil md5:[NSString stringWithFormat:@"%d", arc4random() % 10000]];
}

/**
 * 建议 traceid 字段包含用户信息及订单信息，方便后续对订单状态的查询和跟踪
 */
- (NSString *)genTraceId
{
    return [NSString stringWithFormat:@"crestxu_%@", [self genTimeStamp]];
}

- (NSString *)genOutTradNo
{
    return [CommonUtil md5:[NSString stringWithFormat:@"%d", arc4random() % 10000]];
}

- (NSString *)genPackage
{
    // 构造参数列表
    NSMutableDictionary *params = [NSMutableDictionary dictionary]; 
    [params setObject:@"WX" forKey:@"bank_type"];
    [params setObject:_prodName forKey:@"body"];
    [params setObject:@"1" forKey:@"fee_type"];
    [params setObject:@"UTF-8" forKey:@"input_charset"];
    [params setObject:_notify_url forKey:@"notify_url"];
    [params setObject:_orderNum forKey:@"out_trade_no"];
    [params setObject:WXAPP_PARTNER_ID forKey:@"partner"];
    [params setObject:[CommonUtil getIPAddress:YES] forKey:@"spbill_create_ip"]; 
    [params setObject:_money forKey:@"total_fee"];    // 1 =＝ ¥0.01
    
    NSArray *keys = [params allKeys];
    NSArray *sortedKeys = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { 
        return [obj1 compare:obj2 options:NSNumericSearch];
    }];
    
    // 生成 packageSign
    NSMutableString *package = [NSMutableString string];
    for (NSString *key in sortedKeys) {
        [package appendString:key];
        [package appendString:@"="];
        [package appendString:[params objectForKey:key]];
        [package appendString:@"&"];
    }
    
    [package appendString:@"key="];
    [package appendString:WXAPP_PARTNER_KEY]; // 注意:不能hardcode在客户端,建议genPackage这个过程都由服务器端完成
    
    // 进行md5摘要前,params内容为原始内容,未经过url encode处理
    NSString *packageSign = [[CommonUtil md5:[package copy]] uppercaseString]; 
    package = nil;
    
    // 生成 packageParamsString
    NSString *value = nil;  
    package = [NSMutableString string];
    for (NSString *key in sortedKeys) {
        [package appendString:key];
        [package appendString:@"="];
        value = [params objectForKey:key];
        
        // 对所有键值对中的 value 进行 urlencode 转码
        value = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)value, nil, (CFStringRef)@"!*'&=();:@+$,/?%#[]", kCFStringEncodingUTF8));
        
        [package appendString:value];
        [package appendString:@"&"];
    }
    NSString *packageParamsString = [package substringWithRange:NSMakeRange(0, package.length - 1)];

    NSString *result = [NSString stringWithFormat:@"%@&sign=%@", packageParamsString, packageSign];
    
    JXLog(@"--- Package: %@", result);
    
    return result;
}

- (NSString *)genSign:(NSDictionary *)signParams
{
    // 排序
    NSArray *keys = [signParams allKeys];
    NSArray *sortedKeys = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { 
        return [obj1 compare:obj2 options:NSNumericSearch];
    }];
    
    // 生成
    NSMutableString *sign = [NSMutableString string];
    for (NSString *key in sortedKeys) {
        [sign appendString:key];
        [sign appendString:@"="];
        [sign appendString:[signParams objectForKey:key]];
        [sign appendString:@"&"];
    }
    NSString *signString = [[sign copy] substringWithRange:NSMakeRange(0, sign.length - 1)];
    
    NSString *result = [CommonUtil sha1:signString];
    NSLog(@"--- Gen sign: %@", result);
    return result;
}

- (NSMutableDictionary *)getProductArgs
{
    self.timeStamp = [self genTimeStamp];
    self.nonceStr = [self genNonceStr]; // traceId 由开发者自定义，可用于订单的查询与跟踪，建议根据支付用户信息生成此id
    self.traceId = [self genTraceId];
    
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    [params setObject:WXAPP_ID forKey:@"appid"];
    [params setObject:WXAPP_PAY_SIGN_KEY forKey:@"appkey"];
    [params setObject:self.timeStamp forKey:@"noncestr"];
    [params setObject:self.timeStamp forKey:@"timestamp"];
    [params setObject:_orderNum forKey:@"traceid"];
    [params setObject:[self genPackage] forKey:@"package"];
    [params setObject:[self genSign:params] forKey:@"app_signature"];
    [params setObject:@"sha1" forKey:@"sign_method"];

    return  params;
}

#pragma mark - 主体流程
- (void)getAccessToken
{
    NSString *getAccessTokenUrl = [NSString stringWithFormat:@"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%@&secret=%@", WXAPP_ID, WXAPP_SECRET];
    
    JXLog(@"--- GetAccessTokenUrl: %@", getAccessTokenUrl);
    MBProgressHUD *loding = [DialogUtils showLoadingWithView:self.container];
    [JXHttpTool get:getAccessTokenUrl params:nil success:^(id responseObj) {
        [loding hide:YES];
        NSError *error = nil;
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseObj
                                                             options:kNilOptions
                                                               error:&error];
        if (error) {
            JXLog(@"获取 AccessToken 失败");
            return;
        } else {
            JXLog(@"--- %@", [[NSString alloc]initWithData:responseObj encoding:NSUTF8StringEncoding]);
        }
        
        NSString *accessToken = dict[AccessTokenKey];
        if (accessToken) {
            JXLog(@"--- AccessToken: %@", accessToken);
            [self getPrepayId:accessToken];
        } else {
            NSString *strMsg = [NSString stringWithFormat:@"errcode: %@, errmsg:%@", dict[errcodeKey], dict[errmsgKey]];
            JXLog(@"获取 AccessToken 错误%@",strMsg);
        }

    } failure:^(NSError *error) {
        [loding hide:YES];
        JXLog(@"获取 AccessToken 失败");
    }];

}

- (void)getPrepayId:(NSString *)accessToken
{
    
    NSString *getPrepayIdUrl = [NSString stringWithFormat:@"https://api.weixin.qq.com/pay/genprepay?access_token=%@", accessToken];
    
    JXLog(@"--- GetPrepayIdUrl: %@", getPrepayIdUrl);
    MBProgressHUD *loding = [DialogUtils showLoadingWithView:self.container];
    // 1.获得请求管理者
    AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
    mgr.requestSerializer = [AFJSONRequestSerializer serializer];
    [mgr.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [mgr.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];

    // 2.发送POST请求
    [mgr POST:getPrepayIdUrl parameters:[self getProductArgs] success:^(AFHTTPRequestOperation *operation, id responseObject) {
                [loding hide:YES];
                NSError *error = nil;
                NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:operation.responseData
                                                                     options:kNilOptions
                                                                       error:&error];
                if (error) {
                    JXLog(@"获取 PrePayId 失败");
                    return;
                } else {
                    JXLog(@"--- %@", [[NSString alloc]initWithData:operation.responseData encoding:NSUTF8StringEncoding]);
                }
        
                NSString *prePayId = dict[PrePayIdKey];
                if (prePayId) {
                    JXLog(@"--- PrePayId: %@", prePayId);
        
                    // 调起微信支付
                    PayReq *request   = [[PayReq alloc] init];
                    request.partnerId = WXAPP_PARTNER_ID;
                    request.prepayId  = prePayId;
                    request.package   = @"Sign=WXPay";      // 文档为 `Request.package = _package;` , 但如果填写上面生成的 `package` 将不能支付成功
                    request.nonceStr  = self.nonceStr;
                    request.timeStamp = [self.timeStamp longLongValue];
        
                    // 构造参数列表
                    NSMutableDictionary *params = [NSMutableDictionary dictionary];
                    [params setObject:WXAPP_ID forKey:@"appid"];
                    [params setObject:WXAPP_PAY_SIGN_KEY forKey:@"appkey"];
                    [params setObject:request.nonceStr forKey:@"noncestr"];
                    [params setObject:request.package forKey:@"package"];
                    [params setObject:request.partnerId forKey:@"partnerid"];
                    [params setObject:request.prepayId forKey:@"prepayid"];
                    [params setObject:self.timeStamp forKey:@"timestamp"];
                    request.sign = [self genSign:params];
        
                    // 在支付之前，如果应用没有注册到微信，应该先调用 [WXApi registerApp:appId] 将应用注册到微信
                    [WXApi safeSendReq:request];
                } else {
                    NSString *strMsg = [NSString stringWithFormat:@"errcode: %@, errmsg:%@", dict[errcodeKey], dict[errmsgKey]];
                    JXLog(@"获取 PrePayId 错误%@",strMsg);
                }
        
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
          [loding hide:YES];
          JXLog(@"获取 PrePayId 失败");
    }];
}

@end
